package com.wwdx.service.impl;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.wwdx.BaseCommons;
import com.wwdx.dao.BaseDaoI;
import com.wwdx.model.*;
import com.wwdx.model.dto.BaseActivityDTO;
import com.wwdx.service.TaskServiceI;
import com.wwdx.service.WwdxOrderServiceI;
import com.wwdx.utils.ConstantHolder;
import com.wwdx.utils.Jdbc;
import com.wwdx.utils.Smtp;
import com.wwdx.utils.excel.ExcelUtil;
import com.wwdx.utils.math.BigDecimalUtils;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpUserService;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * @author liguoliang
 * @date 2017-11-16 13:22
 */
@Service
public class TaskServiceImpl extends BaseCommons implements TaskServiceI {

    @Resource
    private BaseDaoI baseDao;

    private List<String> list = new ArrayList<>();
    
    private final String pattern = ConstantHolder.Constant.LOCAL_DATE_PATTERN;

    /**
     * 清理过期会员
     */
    @Override
    public void cleanExpireVip(){
        logger.info("定期清理过期会员");
        String hql = "FROM WwdxVipUserModel WHERE DATEDIFF(expire_time,CURDATE()) < 0";
        List<WwdxVipUserModel> wwdxVipUserModels = wwdxVipUserService.find(hql);
        wwdxVipUserModels.forEach(wwdxVipUserService::delete);
        logger.info("定期清理过期会员,受影响行数：{}",wwdxVipUserModels.size());
    }

    /**
     * 同步微信用户
     */
    @Override
    public void synUserInfo(String nextOpenId) {
        logger.info("开始同步微信用户");
        try {
            WxMpUserService userService = miDuWxService.getUserService();
            WxMpUserList wxMpUserList = userService.userList(nextOpenId);
            List<String> openids = wxMpUserList.getOpenids();
            if(CollectionUtils.isEmpty(openids)){
                HashMap<String, Object> params = Maps.newHashMapWithExpectedSize(2);
                params.put("appid",miDuWxService.getServerConfig().getAppid());
                String hql = "select openid from WwdxWeixinUserModel where appid=:appid";
                List<String> models = wwdxWeixinUserService.get(hql,params);
                Sets.SetView<String> difference = Sets.difference(Sets.newHashSet(models),Sets.newHashSet(list));
                this.executeDeleteUserInfo(difference);
                list.clear();
                return;
            }
            list.addAll(openids);
            nextOpenId = wxMpUserList.getNextOpenid();
            List<WxMpUser> users = Lists.newArrayListWithExpectedSize(openids.size());
            for (String openid : openids) {
                WwdxWeixinUserModel model = wwdxWeixinUserService.getByOpenId(openid);
                if (model == null){
                    users.add(userService.userInfo(openid));
                }
            }
            taskExecutor.execute(()-> this.executeSynUserInfo(users));
            this.synUserInfo(nextOpenId);
        }catch (WxErrorException e){
            logger.error("错误信息，{}",e.getMessage());
        }
    }

    private void executeSynUserInfo(List<WxMpUser> list){
        String appId = miDuWxService.getWxMpConfigStorage().getAppId();
        Jdbc jdbc = new Jdbc();
        Connection conn = jdbc.getConnection();
        PreparedStatement stmt = null;
        int[] result = null;
        try {
            conn.setAutoCommit(false);
            String sql = "INSERT INTO wwdx_weixin_user(openid,appid,unionid,nickname,sex,headimgurl,city,country,province," +
                    "language,subscribe,subscribe_time,gmt_created,gmt_modified) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,now(),now())";
            stmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            for (WxMpUser wxMpUser:list) {
                stmt.setString(1, wxMpUser.getOpenId());
                stmt.setString(2, appId);
                stmt.setString(3, wxMpUser.getUnionId());
                stmt.setString(4, Strings.nullToEmpty(wxMpUser.getNickname()));
                stmt.setInt(5,wxMpUser.getSex() == null ? 0 : wxMpUser.getSex());
                stmt.setString(6,Strings.nullToEmpty(wxMpUser.getHeadImgUrl()));
                stmt.setString(7, Strings.nullToEmpty(wxMpUser.getCity()));
                stmt.setString(8,Strings.nullToEmpty(wxMpUser.getCountry()));
                stmt.setString(9,Strings.nullToEmpty(wxMpUser.getProvince()));
                stmt.setString(10,Strings.nullToEmpty(wxMpUser.getLanguage()));
                stmt.setBoolean(11,true);
                stmt.setLong(12,wxMpUser.getSubscribeTime() == null ? 0 : wxMpUser.getSubscribeTime());
                stmt.addBatch();
            }
            result = stmt.executeBatch();
            conn.commit();
        } catch (SQLException e) {
            logger.error("错误信息，{}",e.getMessage());
        } finally {
            jdbc.closeConnStmtRs(conn, stmt, null);
        }
        logger.info("同步微信用户完成,受影响行数：{}",result.length);
    }

    private void executeDeleteUserInfo(Sets.SetView<String> list){
        Jdbc jdbc = new Jdbc();
        Connection conn = jdbc.getConnection();
        PreparedStatement stmt = null;
        int[] result = null;
        try {
            conn.setAutoCommit(false);
            String sql = "DELETE FROM wwdx_weixin_user WHERE openid=?";
            stmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            for (String openId:list) {
                stmt.setString(1, openId);
                stmt.addBatch();
            }
            result = stmt.executeBatch();
            conn.commit();
        } catch (SQLException e) {
            logger.error("错误信息，{}",e.getMessage());
        } finally {
            jdbc.closeConnStmtRs(conn, stmt, null);
        }
        logger.info("更新微信用户完成,受影响行数：{}",result.length);
    }

    /**
     * 发送邮件
     * @param pre     前缀
     * @param email   邮件
     * @param content 邮件内容
     * @return boolean
     */
    @Override
    public boolean sendEmail(String pre, String email, String content) {
        //文件名称
        String fileName = pre + new LocalDate().minusDays(1) + ".xls";
        String path = ConstantHolder.Constant.EXPORT_PATH + fileName;
        Smtp mail = new Smtp(email, content);
        return mail.sendMail(Lists.newArrayList(new File(path)));
    }

    /**
     * 收入统计
     */
    @Override
    public void exportIncome() {
        List<WwdxInComeModel> list = statisticsInComeService.find("from WwdxInComeModel order by date desc");
        Map<String, String> map = Maps.newLinkedHashMapWithExpectedSize(6);
        map.put("date", "日期");
        map.put("order_day_total", "当天渠道总收入");
        map.put("order_day_user_count", "当天订购人数");
        map.put("order_day_count", "当天订购笔数");
        this.execute(ConstantHolder.Constant.PRE_WWDXINCOME, map, list);
    }

    private void execute(String pre, Map<String, String> map, Collection<?> list) {
        // 文件名称
        String fileName = pre + new LocalDate().minusDays(1) + ".xls";
        String path = ConstantHolder.Constant.EXPORT_PATH + fileName;
        //jdk7 新特性 不需要finally
        try (OutputStream outputStream = new FileOutputStream(path)) {
            ExcelUtil.exportExcel(map, list, outputStream);
        } catch (IOException e) {
            logger.error("错误信息：{}", e.getMessage());
        }
    }

    /**
     * 订单统计
     */
    @Override
    public void exportWwdxOrder() {
        logger.info("统计订单表信息");
        String sql = "select * from wwdx_order where unionid NOT IN (SELECT unionid FROM wwdx_personal WHERE white = 1) order by create_time desc";
        List<WwdxOrderModel> list = wwdxOrderService.excute(wwdxOrderService.findBySqlToListBean(sql, WwdxOrderModel.class));
        Map<String, String> map = Maps.newLinkedHashMapWithExpectedSize(7);
        map.put("strType", "类型");
        map.put("price", "价格");
        map.put("product_name", "产品名称");
        map.put("member_name", "用户名称");
        map.put("channel", "渠道");
        map.put("pay_time", "支付时间");
        map.put("isPay", "是否支付");
        this.execute(ConstantHolder.Constant.PRE_WWDXORDER, map, list);
    }

    @Override
    public void statisticsIncomeExcute() {
        logger.info("收入统计");
        WwdxInComeModel model = new WwdxInComeModel();
        //统计时间节点
        LocalDate localDate = new LocalDate(2017, 10, 1);
        int days = Days.daysBetween(localDate, new LocalDate()).getDays();
        Map<String, Object> params = Maps.newHashMapWithExpectedSize(2);
        //昨天数据时间点
        LocalDate endTime = localDate.plusDays(days - 1);
        params.put("endTime", endTime.toDate());
        //结束日期
        model.setDate(endTime.toDate());
        //当天渠道总收入
        String sql3 = "SELECT IFNULL(SUM(price),0) AS order_day_total FROM wwdx_order w\n" +
                "WHERE w.status = 1 AND w.price >0 AND w.unionid NOT IN (SELECT unionid FROM wwdx_personal WHERE white=1)\n" +
                "AND TO_DAYS(create_time) = TO_DAYS(:endTime)";
        List list3 = baseDao.findBySql(sql3, params);
        Map map3 = (Map) list3.get(0);
        model.setOrder_day_total((BigDecimal) map3.get("order_day_total"));
        //当天订购笔数
        String sql32 = "SELECT w.unionid FROM wwdx_order w\n" +
                "WHERE w.status = 1 AND w.price >0 AND w.unionid NOT IN (SELECT unionid FROM wwdx_personal WHERE white=1)\n" +
                "AND TO_DAYS(create_time) = TO_DAYS(:endTime)";
        List<Map> list32 = baseDao.findBySql(sql32, params);
        int size = list32.size();
        ArrayList<String> unionids = Lists.newArrayListWithExpectedSize(size);
        list32.forEach(map -> unionids.add(map.get("unionid").toString()));
        //当天订购人数
        model.setOrder_day_user_count((long)new HashSet<>(unionids).size());
        model.setOrder_day_count((long) size);
        statisticsInComeService.save(model);
    }

    /**
     * 分销商数据统计
     */
    @Override
    public void executeMerchantProductStatistics(){
        logger.info("分销商数据统计");
        List<WwdxMerchantProductModel> wwdxMerchantProductModels = wwdxMerchantProductService.find("from WwdxMerchantProductModel");
        List<BaseActivityDTO.StatisticsMerchantIncomeDTO> statisticsMerchantIncomeDTOS = Lists.newArrayListWithExpectedSize(16);
        for (WwdxMerchantProductModel wwdxMerchantProductModel : wwdxMerchantProductModels) {
            BaseActivityDTO.StatisticsMerchantIncomeDTO dto = new BaseActivityDTO.StatisticsMerchantIncomeDTO();
            //商户ID
            String merchantId = wwdxMerchantProductModel.getMerchant_id();
            Integer scale = 0;
            WwdxMerchantModel wwdxMerchantModel = wwdxMerchantService.getById(merchantId);
            if (wwdxMerchantModel != null){
                scale = wwdxMerchantModel.getScale();
            }
            //商户下的产品ID
            Long productId = wwdxMerchantProductModel.getProduct_id();
            String pvkey = "";
            BigDecimal price = new BigDecimal(0);
            LocalDate localDate = new LocalDate().minusDays(1);
            String date = localDate.toString(ConstantHolder.Constant.LOCAL_DATE_PATTERN);
            WwdxVipProductModel wwdxVipProductModel = wwdxVipProductService.getByProductId(productId);
            WwdxThemeModel wwdxThemeModel = wwdxThemeService.getByThemeId(productId);
            if (wwdxVipProductModel != null){
                pvkey = String.format(ConstantHolder.MERCHANT_VIP_PV_LIST, merchantId, date);
                price = wwdxVipProductModel.getPrice();
            }else if (wwdxThemeModel != null){
                pvkey = String.format(ConstantHolder.MERCHANT_THEME_PV_LIST, merchantId, date,productId);
                price = wwdxThemeModel.getPrice();
            }
            //PV
            dto.setPvCount(listOperations.size(pvkey));
            //UV
            dto.setUvCount((long)new HashSet<>(listOperations.range(pvkey,0,-1)).size());
            //商户名称
            dto.setMerchant_id(merchantId);
            //商户产品ID
            dto.setProduct_id(productId);
            //时间
            dto.setDate(date);
            //商品单价
            dto.setPrice(price);
            //分成比例
            dto.setScale(scale);
            WwdxOrderServiceI.MerchantProductStatisticsDTO merchantProductDTO = wwdxOrderService.getMerchantProductDTO(merchantId,productId, localDate.toDate());
            //订单收益
            BigDecimal bigDecimal = BigDecimalUtils.calculate(merchantProductDTO.getTotal()).multiply(scale).div(100).getBigDecimal();
            dto.setPriceCount(bigDecimal);
            //订单人数
            dto.setUserCount(merchantProductDTO.getCount());
            statisticsMerchantIncomeDTOS.add(dto);
        }
        //保存数据至商户统计表中
        this.insertMerchantProductStatistics(statisticsMerchantIncomeDTOS);
    }

    private void insertMerchantProductStatistics(List<BaseActivityDTO.StatisticsMerchantIncomeDTO> list){
        Jdbc jdbc = new Jdbc();
        Connection conn = jdbc.getConnection();
        PreparedStatement stmt = null;
        int[] result = null;
        try {
            conn.setAutoCommit(false);
            String sql = "INSERT INTO wwdx_merchant_product_statistics (merchant_id,product_id,date,pvCount,uvCount," +
                    "userCount,scale,price,priceCount,gmt_created) VALUES (?,?,?,?,?,?,?,?,?,now())";
            stmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            for (BaseActivityDTO.StatisticsMerchantIncomeDTO dto:list) {
                stmt.setString(1, dto.getMerchant_id());
                stmt.setLong(2, dto.getProduct_id());
                stmt.setString(3, dto.getDate());
                stmt.setLong(4, dto.getPvCount());
                stmt.setLong(5,dto.getUvCount());
                stmt.setLong(6,dto.getUserCount());
                stmt.setInt(7, dto.getScale());
                stmt.setBigDecimal(8,dto.getPrice());
                stmt.setBigDecimal(9,dto.getPriceCount());
                stmt.addBatch();
            }
            result = stmt.executeBatch();
            conn.commit();
        } catch (SQLException e) {
            logger.error(e.getMessage());
        } finally {
            jdbc.closeConnStmtRs(conn, stmt, null);
        }
        logger.info("分销商数据统计操作完成,受影响行数：" + result.length);
    }

    /**
     * 页面数据统计
     */
    @Override
    public void executePageStatistics() {
        logger.info("页面数据统计");
        LocalDate localDate = new LocalDate().minusDays(1);
        String date = localDate.toString(pattern);
        List<String> list = Lists.newArrayList(ConstantHolder.Constant.IOS, ConstantHolder.Constant.WEIXIN,
                ConstantHolder.Constant.ANDROID, ConstantHolder.Constant.WXMA);
        WwdxPageStatisticsModel model = new WwdxPageStatisticsModel();
        for (String channel : list) {
            Set<String> keys = redisTemplate.keys(String.format(ConstantHolder.PAGE_PV_LIST, date, channel, "*"));
            for (String type : keys) {
                String[] str = type.split(":");
                type = str[str.length - 1].trim();
                String name = "";
                switch (type) {
                    case "1":
                        name = "首页";
                        break;
                    case "2":
                        name = "搜索";
                        break;
                    case "3":
                        name = "赠一得一";
                        break;
                    case "4":
                        name = "会员";
                        break;
                    case "5":
                        name = "书单列表";
                        break;
                    case "6":
                        name = "书单详情";
                        break;
                    case "7":
                        name = "解读书库";
                        break;
                    case "8":
                        name = "精品课列表";
                        break;
                    case "9":
                        name = "课程详情页";
                        break;
                    case "10":
                        name = "小程序首页";
                        break;
                    case "11":
                        name = "我的";
                        break;
                    case "12":
                        name = "优惠卷";
                        break;
                    case "13":
                        name = "小课";
                        break;
                    case "14":
                        name = "音频下载列表";
                        break;
                    default:
                        break;
                }
                try {
                    model = (WwdxPageStatisticsModel) model.clone();
                    String pvKey = String.format(ConstantHolder.PAGE_PV_LIST, date, channel, type);
                    model.setDate(date);
                    model.setType(Integer.valueOf(type));
                    model.setChannel(channel);
                    model.setName(name);
                    model.setPv(listOperations.size(pvKey));
                    model.setUv((long) new HashSet<>(listOperations.range(pvKey, 0, -1)).size());
                    wwdxPageStatisticsService.save(model);
                }catch (CloneNotSupportedException e){
                    logger.error("错误信息：{}",e.getMessage());
                }
            }
        }
    }

    /**
     * 赠一得一按渠道统计
     */
    @Override
    public void giveonechannel() {
        logger.info("赠一得一按渠道统计");
        LocalDate localDate = new LocalDate().minusDays(1);
        String date = localDate.toString(pattern);
        List<String> list = Lists.newArrayList(ConstantHolder.Constant.WEIXIN, ConstantHolder.Constant.ANDROID, ConstantHolder.Constant.IOS);
        for (String channel : list) {
            WwdxGiveoneChannelModel model = new WwdxGiveoneChannelModel();
            //pv
            String pvKey = String.format(ConstantHolder.GIVEONE_STATISTICS_CHANNEL_PV_LIST, date, channel);
            //领取人数
            String giveUserKey = String.format(ConstantHolder.GIVEONE_STATISTICS_GIVE_CHANNEL_USER_LIST, date, channel);
            //领取书籍数
            String giveBookKey = String.format(ConstantHolder.GIVEONE_STATISTICS_GIVE_CHANNEL_BOOK_SET, date, channel);
            model.setDate(date);
            model.setChannel(channel);
            model.setPvCount(listOperations.size(pvKey));
            model.setUvCount((long) new HashSet<>(listOperations.range(pvKey, 0, -1)).size());
            model.setGiveUserCount(listOperations.size(giveUserKey));
            model.setGiveBookCount(setOperations.size(giveBookKey));
            wwdxGiveoneChannelService.save(model);
        }
    }

    /**
     * 赠一得一按书籍统计
     */
    @Override
    public void giveonebook() {
        logger.info("赠一得一按书籍统计");
        LocalDate localDate = new LocalDate().minusDays(1);
        String date = localDate.toString(pattern);
        Set<String> keys = redisTemplate.keys(String.format(ConstantHolder.GIVEONE_STATISTICS_BOOK_PV_LIST, date, "*"));
        for (String bookid : keys) {
            String[] str = bookid.split(":");
            bookid = str[str.length - 1];
            //书籍信息
            WwdxBookModel wwdxBookModel = wwdxBookService.getByBookId(Long.valueOf(bookid));
            if (wwdxBookModel == null) {
                continue;
            }
            WwdxGiveoneBookModel model = new WwdxGiveoneBookModel();
            model.setBookname(wwdxBookModel.getBookname());
            //pv
            String pvKey = String.format(ConstantHolder.GIVEONE_STATISTICS_BOOK_PV_LIST, date, bookid);
            //领取人数
            String giveUserKey = String.format(ConstantHolder.GIVEONE_STATISTICS_BOOK_USER_LIST, date, bookid);
            model.setDate(date);
            model.setPvCount(listOperations.size(pvKey));
            model.setUvCount((long) new HashSet<>(listOperations.range(pvKey, 0, -1)).size());
            model.setGiveUserCount(listOperations.size(giveUserKey));
            wwdxGiveoneBookService.save(model);
        }
    }

    /**
     * 赠一得一按用户统计
     */
    @Override
    public void giveoneuser() {
        logger.info("赠一得一按用户统计");
        LocalDate localDate = new LocalDate().minusDays(1);
        String date = localDate.toString(pattern);
        Set<String> keys = redisTemplate.keys(String.format(ConstantHolder.GIVEONE_STATISTICS_USER_PV_LIST, date, "*"));
        for (String unionid : keys) {
            String[] str = unionid.split(":");
            unionid = str[str.length - 1];
            //用户信息
            WwdxPersonalModel wwdxPersonalModel = wwdxPersonalService.getByUnionid(unionid);
            if (wwdxPersonalModel == null) {
                continue;
            }
            WwdxGiveoneUserModel model = new WwdxGiveoneUserModel();
            model.setNickname(wwdxPersonalModel.getNickname());
            //pv
            String pvKey = String.format(ConstantHolder.GIVEONE_STATISTICS_USER_PV_LIST, date, unionid);
            model.setDate(date);
            model.setPvCount(listOperations.size(pvKey));
            wwdxGiveoneUserService.save(model);
        }
    }

    /**
     * 早读听书打卡统计
     */
    @Override
    public void cardReading() {
        logger.info("早读听书打卡统计");
        LocalDate localDate = new LocalDate().minusDays(1);
        String date = localDate.toString();
        WwdxCardModel model = WwdxCardModel.builder()
                .date(date).userDayCount(setOperations.size(String.format(ConstantHolder.USER_CARD_DAY_SET, date)))
                .userNewCount(setOperations.size(String.format(ConstantHolder.USER_CARD_NEW_SET,date)))
                .userTotalCount(setOperations.size(ConstantHolder.USER_CARD_TOTAL_SET)).type(0).build();
        wwdxCardService.save(model);
    }

    /**
     * 首页订阅统计统计
     */
    @Override
    public void homeSubscribe() {
        logger.info("首页订阅统计");
        LocalDate localDate = new LocalDate().minusDays(1);
        String date = localDate.toString();
        WwdxCardModel model = WwdxCardModel.builder()
                .date(date).userDayCount(0L)
                .userNewCount(setOperations.size(String.format(ConstantHolder.USER_SUBSCRIBE_NEW_SET,date)))
                .userTotalCount(setOperations.size(ConstantHolder.USER_SUBSCRIBE_TOTAL_SET)).type(1).build();
        wwdxCardService.save(model);
    }
}